package com.smsc.headend.task.engine.handler.impl.generalparam;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.constant.RequestSource;
import com.smsc.headend.common.enums.errorcode.TaskHandlerErrorCode;
import com.smsc.headend.common.utils.CustomStringUtils;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.data.dto.DLMSSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.query.ModelDataItemQueryItem;
import com.smsc.headend.module.mdm.ws.WsTokenResultEnum;
import com.smsc.headend.module.protocol.DeviceData;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.protocol.cosem.ic.TokenGateway;
import com.smsc.headend.module.task.consts.TaskKafkaTopic;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.DeviceActionType;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskHandlerException;
import com.smsc.headend.task.engine.handler.TaskHandler;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.utils.WsTokenMapUtil;
import gurux.dlms.enums.ObjectType;
import org.assertj.core.util.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

/**
 * @author wxh
 * @date 2021/5/11 14:14
 */
public class ActionGeneralParamHandler implements TaskHandler {
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    KafkaTemplate kafkaTemplate;

    @Override
    public int handle(Task task, AtomicTask atomicTask, DeviceData deviceData, String actionResult) throws UDISTaskHandlerException {
        if (!(deviceData instanceof CosemData)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.DEVICE_DATA_TYPE_NOT_SUPPORT.toString());
        }
        log.info("{} action result {}", atomicTask.getAtomicTaskNo(), actionResult);
        Meter meter = assetManagementService.getMeterById(task.getMeterId());
        CosemData cosemData = (CosemData) deviceData;
        Map<String, Object> param = getParameterMap(task, atomicTask);
        Long subDataItemId = Long.valueOf(String.valueOf(param.get("subDataItemId"))) ;
        List<DataItemDTO> dataItemDTOS = getModelDataItemCosems(meter.getModelId(), Lists.newArrayList(subDataItemId), DeviceActionType.Action.getCode());
        if (CollUtil.isEmpty(dataItemDTOS)) {
            throw new UDISTaskHandlerException(TaskHandlerErrorCode.NO_TASK_DATA_ITEM.toString());
        }
        handleCosemData(dataItemDTOS.get(0), task, atomicTask, cosemData);
        return 1;
    }

    private void handleCosemData(DataItemDTO dataItemDTO, Task task, AtomicTask atomicTask, CosemData cosemData) throws UDISTaskHandlerException {
        Integer classId = ((DLMSSubDataItemDTO) dataItemDTO).getClassId();
        ObjectType objectType = ObjectType.forValue(classId);
        switch (objectType) {
            case TOKEN_GATEWAY:
                processTokenGetewayResult(task, atomicTask, cosemData, dataItemDTO);
                break;
            default:
                throw new UDISTaskHandlerException(TaskHandlerErrorCode.NO_IMPL_OBJECT_TYPE.toString() + classId);
        }
    }

    private void processTokenGetewayResult(Task task, AtomicTask atomicTask, CosemData cosemData, DataItemDTO dataItemDTO) {
        List<CosemData> childDatas = cosemData.getChild();
        Integer statusCode = null;
        String bitStr = null;
        if (childDatas.size() == 2) {
            statusCode = Convert.toInt(cosemXmlService.getData(childDatas.get(0).getType(), childDatas.get(0).getValue()));
            bitStr = (String) cosemXmlService.getData(childDatas.get(1).getType(), childDatas.get(1).getValue());
        }
        if (statusCode == null) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(dataItemDTO.getSubDataItemId()), "Failed", 600l, Boolean.FALSE);
            return;
        }
        if (statusCode == TokenGateway.StatusCode.TOKEN_EXECUTION_OK.getStatusCode()) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(dataItemDTO.getSubDataItemId()), TokenGateway.StatusCode.TOKEN_EXECUTION_OK.name(), 600l, Boolean.TRUE);
        } else {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(dataItemDTO.getSubDataItemId()), TokenGateway.StatusCode.getByCode(statusCode) == null ? "Failed":TokenGateway.StatusCode.getByCode(statusCode), 600l, Boolean.FALSE);
        }
        if (StrUtil.equals(TaskType.WsMdmTokenAction.toString(), task.getTaskType())) {
            handleVendingTokenResult(task, atomicTask, statusCode, bitStr, true, "");
        }
    }

    @Override
    public int handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        Map<String, Object> param = getParameterMap(task, atomicTask);
        Long subDataItemId = Long.valueOf(String.valueOf(param.get("subDataItemId"))) ;
        if (task.getIsODR()) {
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(subDataItemId), message, 600l, Boolean.FALSE);
        }

        if (StrUtil.equals(TaskType.WsMdmTokenAction.toString(), task.getTaskType())) {
            handleVendingTokenResult(task, atomicTask, null, null, false, message);
        }
        return -1;
    }

    private List<DataItemDTO> getModelDataItemCosems(Long modelId, List<Long> dataItemIds, Integer deviceActionTypeCode) {
        ModelDataItemQueryItem modelDataItemQueryItem = new ModelDataItemQueryItem();
        modelDataItemQueryItem.setModelId(modelId);
        modelDataItemQueryItem.setDataItemIds(dataItemIds);
        modelDataItemQueryItem.setDeviceActionType(deviceActionTypeCode);

        return assetManagementService.queryModelDataItemCosem(modelDataItemQueryItem);
    }

    private String getAtomicTaskValue(AtomicTask atomicTask) {
        Map<String, Object> map = JSONUtil.toBean(atomicTask.getAtomicTaskParam(), Map.class);
        return (String) map.get("value");
    }

    /**
     * 处理Vending Token webservice 接口
     * @param task
     * @param atomicTask
     * @param statusCode
     * @param bitStr
     */
    private void handleVendingTokenResult(Task task, AtomicTask atomicTask, Integer statusCode, String bitStr, boolean isSuccess, String failedMessage) {
        Map<String, Object> taskMap = JSONUtil.toBean(task.getParamJson(), Map.class);
        if (isSuccess) {
            String wsVendingTokenResult = RedisKeys.getRequestWsVendingToken(task.getTaskNo());
            String lastTokenResult = (String) redisUtils.hget(wsVendingTokenResult, "1");
            String source = (String)taskMap.get("source");
            int replyTokenCode;
            if (StrUtil.equalsIgnoreCase(RequestSource.UNIFIED.name(), source) && statusCode == 3) {
                // 如果是Unified系统过来的只判断statusCode为3的则为成功
                replyTokenCode = 0;
            }else {
                replyTokenCode = WsTokenMapUtil.getResultCode(statusCode, CustomStringUtils.bitStringToHexString(bitStr));
            }
            String tokenStr = "";
            if (StrUtil.isNotEmpty(lastTokenResult)) {
                tokenStr = lastTokenResult + "," + getAtomicTaskValue(atomicTask) + ":" + replyTokenCode;
            } else {
                tokenStr = getAtomicTaskValue(atomicTask) + ":" + replyTokenCode;
            }
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)) {
                // send to adapter
                JSONObject obj = JSONUtil.createObj();
                String tokenXmlStr = (String) taskMap.get("vendingXmlRequest");
                obj.set("VendingType", "token");
                obj.set("vendingXmlRequest", tokenXmlStr);
                obj.set("tokenResult", tokenStr);
                String[] tokenResults = tokenStr.split(",");
                boolean isTokenSuccess = false;
                for (String result: tokenResults) {
                    String rpCode = result.split(":")[1];
                    if (!StrUtil.equalsIgnoreCase(rpCode, Convert.toStr(WsTokenResultEnum.TOKEN_PROC_SUCCESS.code()))) {
                        isTokenSuccess = false;
                        break;
                    }
                    isTokenSuccess = true;
                }
                if (isTokenSuccess) {
                    obj.set("replyCode", "0.0");
                } else {
                    obj.set("replyCode", "0.2");
                }
                kafkaTemplate.send(TaskKafkaTopic.TASK_VENDING_RESULT_TO_ADAPTER, JSONUtil.toJsonStr(obj).getBytes(Charset.forName("utf-8")));
            } else {
                // 不是最后一个任务时, 把token result存到redis
                redisUtils.hset(RedisKeys.getRequestWsVendingToken(task.getTaskNo()), "1", tokenStr, 600);
            }
        } else {
            String wsVendingTokenResult = RedisKeys.getRequestWsVendingToken(task.getTaskNo());
            String lastTokenResult = (String) redisUtils.hget(wsVendingTokenResult, "1");
            String errorMessage = "";
            if (isSubTaskGroupLastAtomicTask(task, atomicTask)) {
                if (StrUtil.isNotEmpty(lastTokenResult)) {
                    errorMessage = lastTokenResult + "," + getAtomicTaskValue(atomicTask) + ":" + failedMessage;
                } else {
                    errorMessage = getAtomicTaskValue(atomicTask) + ":" + failedMessage;
                }
                // send to adapter
                JSONObject obj = JSONUtil.createObj();
                String tokenXmlStr = (String) taskMap.get("vendingXmlRequest");
                obj.set("VendingType", "token");
                obj.set("vendingXmlRequest", tokenXmlStr);
                obj.set("errorMessage", errorMessage);
                obj.set("replyCode", "0.2");

                kafkaTemplate.send(TaskKafkaTopic.TASK_VENDING_RESULT_TO_ADAPTER, JSONUtil.toJsonStr(obj).getBytes(Charset.forName("utf-8")));
            } else {
                // 不是最后一个任务时, 把token result存到redis
                redisUtils.hset(RedisKeys.getRequestWsVendingToken(task.getTaskNo()), "1", failedMessage, 600);
            }
        }
    }
}
